package com.vinny.springbootflowable;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.vinny.springbootflowable.common.ApiResponse;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.task.api.Task;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest
@AutoConfigureMockMvc
class ExpenseProcessTestForManager {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    @WithMockUser(username = "employee", roles = {"EMPLOYEE"})
    void testManagerApprovalProcess() throws Exception {
        // 1. 员工提交报销申请（金额小于500）
        Map<String, Object> expenseRequest = new HashMap<>();
        expenseRequest.put("amount", 300.0);
        expenseRequest.put("description", "办公用品采购");

        MvcResult submitResult = mockMvc.perform(post("/expense/api/submit")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(expenseRequest)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.message").value("提交成功"))
                .andReturn();

        // 获取流程实例ID
        String responseJson = submitResult.getResponse().getContentAsString();
        ApiResponse<?> response = objectMapper.readValue(responseJson, ApiResponse.class);
        String processInstanceId = (String) response.getData();
        assertNotNull(processInstanceId);

        // 员工查看第一次提交后的流程图
        mockMvc.perform(get("/expense/processDiagram")
                .param("processInstanceId", processInstanceId)
                .with(SecurityMockMvcRequestPostProcessors.user("employee").roles("EMPLOYEE")))
                .andExpect(status().isOk())
                .andExpect(content().contentType("image/png"));

        // 2. 经理查看待办任务
        mockMvc.perform(get("/expense/api/tasks")
                .with(SecurityMockMvcRequestPostProcessors.user("manager").roles("MANAGER")))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data").isArray());

        // 获取经理的任务
        List<Task> managerTasks = taskService.createTaskQuery()
                .taskCandidateOrAssigned("manager")
                .processInstanceId(processInstanceId)
                .list();
        assertFalse(managerTasks.isEmpty());
        Task managerTask = managerTasks.get(0);

        // 经理查看审批前的流程图
        mockMvc.perform(get("/expense/processDiagram")
                .param("processInstanceId", processInstanceId)
                .with(SecurityMockMvcRequestPostProcessors.user("manager").roles("MANAGER")))
                .andExpect(status().isOk())
                .andExpect(content().contentType("image/png"));

        // 3. 经理首次审批（驳回）
        Map<String, Object> rejectRequest = new HashMap<>();
        rejectRequest.put("outcome", "驳回");
        rejectRequest.put("comment", "请补充详细的采购清单和用途说明");

        mockMvc.perform(post("/expense/api/complete/" + managerTask.getId())
                .with(SecurityMockMvcRequestPostProcessors.user("manager").roles("MANAGER"))
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(rejectRequest)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.message").value("审批完成"));

        // 4. 员工查看待办任务
        mockMvc.perform(get("/expense/api/tasks")
                .with(SecurityMockMvcRequestPostProcessors.user("employee").roles("EMPLOYEE")))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data").isArray());

        // 获取员工的任务
        List<Task> employeeTasks = taskService.createTaskQuery()
                .taskAssignee("employee")
                .processInstanceId(processInstanceId)
                .list();
        assertFalse(employeeTasks.isEmpty());
        Task employeeTask = employeeTasks.get(0);

        // 员工查看被驳回后的流程图
        mockMvc.perform(get("/expense/processDiagram")
                .param("processInstanceId", processInstanceId)
                .with(SecurityMockMvcRequestPostProcessors.user("employee").roles("EMPLOYEE")))
                .andExpect(status().isOk())
                .andExpect(content().contentType("image/png"));

        // 5. 员工修改并重新提交
        Map<String, Object> resubmitRequest = new HashMap<>();
        resubmitRequest.put("amount", 300.0);
        resubmitRequest.put("description", "办公用品采购：打印纸 10包，签字笔 20支，文件夹 30个，用于日常办公");

        mockMvc.perform(post("/expense/api/resubmit/" + employeeTask.getId())
                .with(SecurityMockMvcRequestPostProcessors.user("employee").roles("EMPLOYEE"))
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(resubmitRequest)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.message").value("重新提交成功"));

        // 6. 经理再次查看待办任务
        List<Task> managerReTasks = taskService.createTaskQuery()
                .taskCandidateOrAssigned("manager")
                .processInstanceId(processInstanceId)
                .list();
        assertFalse(managerReTasks.isEmpty());
        Task managerReTask = managerReTasks.get(0);

        // 经理查看二次审批前的流程图
        mockMvc.perform(get("/expense/processDiagram")
                .param("processInstanceId", processInstanceId)
                .with(SecurityMockMvcRequestPostProcessors.user("manager").roles("MANAGER")))
                .andExpect(status().isOk())
                .andExpect(content().contentType("image/png"));

        // 7. 经理二次审批（通过）
        Map<String, Object> approvalRequest = new HashMap<>();
        approvalRequest.put("outcome", "通过");
        approvalRequest.put("comment", "采购清单明确，用途合理，同意报销");

        mockMvc.perform(post("/expense/api/complete/" + managerReTask.getId())
                .with(SecurityMockMvcRequestPostProcessors.user("manager").roles("MANAGER"))
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(approvalRequest)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.message").value("审批完成"));

        // 8. 验证流程状态
        mockMvc.perform(get("/expense/api/process/" + processInstanceId)
                .with(SecurityMockMvcRequestPostProcessors.user("manager").roles("MANAGER")))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.status").value("已完成"));

        // 9. 验证流程历史记录
        mockMvc.perform(get("/expense/api/comments/" + processInstanceId)
                .with(SecurityMockMvcRequestPostProcessors.user("manager").roles("MANAGER")))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data").isArray())
                .andExpect(jsonPath("$.data.length()").value(4)); // 提交、驳回、重新提交、通过，共4条记录

        // 10. 验证流程确实结束
        assertEquals(0, taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .count());
    }
} 